using System.Collections.Generic;
using UnityEngine;

namespace IQIGame.Onigao.GamePlay
{
    public class SmallPetFollowMoveBehavior : SmallPetBaseMoveBehavior
    {
        public float startFollowDistance { get; set; } = 3f;

        public float stopFollowDistance { get; set; } = 1f;

        public float startBackDistance { get; set; } = 0.5f;

        public float stopBackDistance { get; set; } = 1.5f;

        public float playerSpeed => entity.owner.defaultMeterSpeed * _ownerSpeedPower;

        public const float MaxDistance = 10f;

        public SmallPetAnimationComponent animationComponent { get; private set; }

        public override void Init()
        {
            base.Init();
            _stateMachine.Init();
            _stateMachine.owner = this;
            _stateMachine.ChangeState((int)FollowMoveBehaviorStateMachine.State.Idle);
            animationComponent =
                entity.GetComponent<SmallPetAnimationComponent>(ETComponentType.SmallPetAnimationComponent);
            var playerForward = entity.owner.transform.forward;
            var endPos = entity.owner.transform.position -
                         new Vector3(playerForward.x, 0, playerForward.z).normalized;
            entity.transform.position = endPos;
        }

        public override void OnUpdate(float dt)
        {
            _stateMachine.OnUpdate(dt);
        }

        public override uint MaxStore => 4;

        public override void OnReset()
        {
            base.OnReset();
            entity = null;
            animationComponent = null;
            _stateMachine.owner = null;
            _stateMachine.DeInit();
            startFollowDistance = 3f;
            stopFollowDistance = 1f;
            startBackDistance = 0.5f;
            stopBackDistance = 1.5f;
            _ownerSpeedPower = 0f;
        }

        public override void OnOwnerMove(Vector3 pos, float speedPower)
        {
            _ownerSpeedPower = speedPower;
        }

        private readonly StateMachine<SmallPetFollowMoveBehavior> _stateMachine = new FollowMoveBehaviorStateMachine();

        private readonly FollowMoveBehaviorState _idleState;

        private readonly FollowMoveBehaviorState _followState;

        private readonly FollowMoveBehaviorState _backState;

        private float _ownerSpeedPower;
    }

    public abstract class StateMachine<T>
    {
        public T owner { get; set; }

        public abstract void Init();

        public virtual void DeInit()
        {
            owner = default;
            curStateId = 0;
            states.Clear();
        }

        public void ChangeState(int stateId)
        {
            if (stateId == curStateId)
            {
                return;
            }

            var curState = states[curStateId];
            curState?.OnExit();
            var nextState = states[stateId];
            nextState?.OnEnter();
            curStateId = stateId;
        }

        public void OnUpdate(float dt)
        {
            states[curStateId]?.OnUpdate(dt);
        }

        public int curStateId { get; private set; }
        protected readonly Dictionary<int, IState<T>> states = new();
    }

    public class FollowMoveBehaviorStateMachine : StateMachine<SmallPetFollowMoveBehavior>
    {
        public enum State
        {
            Idle,
            Follow,
            Back,
        }

        public override void Init()
        {
            states.Add((int)State.Idle, new IdleState(this));
            states.Add((int)State.Follow, new FollowState(this));
            states.Add((int)State.Back, new BackState(this));
        }
    }

    public interface IState<T>
    {
        T owner { get; }
        public StateMachine<T> stateMachine { get; }
        void OnEnter();
        void OnExit();
        void OnUpdate(float dt);
    }

    public abstract class FollowMoveBehaviorState : IState<SmallPetFollowMoveBehavior>
    {
        protected FollowMoveBehaviorState(StateMachine<SmallPetFollowMoveBehavior> stateMachine)
        {
            this.stateMachine = stateMachine;
        }

        public SmallPetFollowMoveBehavior owner => stateMachine.owner;
        public StateMachine<SmallPetFollowMoveBehavior> stateMachine { get; }

        public virtual void OnEnter()
        {
        }

        public virtual void OnExit()
        {
        }

        public abstract void OnUpdate(float dt);
    }

    public class FollowState : FollowMoveBehaviorState
    {
        public FollowState(StateMachine<SmallPetFollowMoveBehavior> stateMachine) :
            base(stateMachine)
        {
        }

        public override void OnUpdate(float dt)
        {
            var playerPos = owner.entity.owner.transform.position;
            var dir = (playerPos - owner.entity.transform.position).normalized;
            var distance = Vector3.Distance(owner.entity.transform.position, playerPos);
            if (distance > SmallPetFollowMoveBehavior.MaxDistance)
            {
                var playerForward = owner.entity.owner.transform.forward;
                var endPos = owner.entity.owner.transform.position -
                             new Vector3(playerForward.x, 0, playerForward.z).normalized;
                owner.entity.transform.position = endPos;
                stateMachine.ChangeState((int)FollowMoveBehaviorStateMachine.State.Idle);
                return;
            }

            var speed = CalculateSpeed(distance);
            var targetPos = owner.entity.transform.position + dir * (speed * dt);
            GameplayAreaManager.Instance.curSceneArea.navigationCtrl.TryMove(owner.entity.transform.position, targetPos,
                out var realEndPos);
            if (Vector3.Distance(realEndPos, playerPos) < owner.stopFollowDistance)
            {
                stateMachine.ChangeState((int)FollowMoveBehaviorStateMachine.State.Idle);
                return;
            }

            owner.animationComponent.SetMoveSpeed(speed);
            owner.entity.transform.forward = dir;
            owner.entity.transform.position = realEndPos;
        }

        private float CalculateSpeed(float distance)
        {
            return owner.playerSpeed * distance * 0.6f;
        }
    }

    public class BackState : FollowMoveBehaviorState
    {
        public BackState(StateMachine<SmallPetFollowMoveBehavior> stateMachine) :
            base(stateMachine)
        {
        }

        public override void OnUpdate(float dt)
        {
            var playerPos = owner.entity.owner.transform.position;
            var dir = (owner.entity.transform.position - playerPos).normalized;
            var distance = Vector3.Distance(owner.entity.transform.position, playerPos);
            var speed = CalculateSpeed(distance);
            var targetPos = owner.entity.transform.position + dir * (speed * dt);
            GameplayAreaManager.Instance.curSceneArea.navigationCtrl.TryMove(owner.entity.transform.position, targetPos,
                out var realEndPos);
            if (Vector3.Distance(realEndPos, playerPos) > owner.stopBackDistance)
            {
                stateMachine.ChangeState((int)FollowMoveBehaviorStateMachine.State.Follow);
                return;
            }
            //小宠物可能被玩家逼到墙角,这时候就不播动画了
            if (Vector3.Distance(realEndPos, owner.entity.transform.position) > 0.01f)
            {
                owner.animationComponent.SetMoveSpeed(speed);
            }
            else
            {
                owner.animationComponent.SetMoveSpeed(0);
            }
            owner.entity.transform.forward = dir;
            owner.entity.transform.position = realEndPos;
        }

        private float CalculateSpeed(float distance)
        {
            distance = Mathf.Max(distance, 0.5f);
            return owner.playerSpeed / distance;
        }
    }

    public class IdleState : FollowMoveBehaviorState
    {
        public IdleState(StateMachine<SmallPetFollowMoveBehavior> stateMachine) :
            base(stateMachine)
        {
        }

        public override void OnUpdate(float dt)
        {
            owner.entity.GetComponent<EntityAnimationComponent>(ETComponentType.SmallPetAnimationComponent)
                .SetMoveSpeed(0);
            var playerPos = owner.entity.owner.transform.position;
            var distance = Vector3.Distance(owner.entity.transform.position, playerPos);
            if (distance < owner.startBackDistance)
            {
                stateMachine.ChangeState((int)FollowMoveBehaviorStateMachine.State.Back);
            }

            if (distance > owner.startFollowDistance)
            {
                stateMachine.ChangeState((int)FollowMoveBehaviorStateMachine.State.Follow);
            }
        }
    }
}